package com.example.compose


import android.graphics.PointF
import android.os.Build
import androidx.annotation.RequiresApi
import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.tween
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawWithCache
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Path
import androidx.compose.ui.graphics.drawscope.Fill
import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.compose.ui.graphics.drawscope.clipRect
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import java.math.BigDecimal
import java.time.LocalDate

@RequiresApi(Build.VERSION_CODES.O)
@Preview
@Composable
fun SmoothLineGraph() {
    Box(
        modifier = Modifier
            .background(color = PurpleBackgroundColor)
            .fillMaxSize()
    ) {
        val animationProgress = remember { Animatable(0f) }

        LaunchedEffect(key1 = graphData, block = {
            animationProgress.animateTo(targetValue = 1f, tween(durationMillis = 3000))
        })
        Spacer(
            modifier = Modifier
                .padding(all = 8.dp)
                .aspectRatio(ratio = 3 / 2f)
                .fillMaxSize()
                .align(alignment = Alignment.Center)
                .drawWithCache {
                    val path = generateSmoothPath(graphData, size)
                    val filledPath = Path()
                    filledPath.addPath(path)
                    filledPath.lineTo(x = size.width, y = size.height)
                    filledPath.lineTo(x = 0f, y = size.height)
                    filledPath.close()
                    val brush = Brush.verticalGradient(
                        listOf(
                            Color.Green.copy(alpha = 0.4f),
                            Color.Transparent
                        )
                    )
                    onDrawBehind {
                        val barWidthPX = 1.dp.toPx()
                        // 外矩形
                        drawRect(color = Color(0xFFADAEAF), style = Stroke(width = barWidthPX))

                        // 4条竖线
                        val verticalLines = 4
                        val verticalSize = size.width / (verticalLines + 1)
                        repeat(verticalLines) { i ->
                            val startX = verticalSize * (i + 1)
                            drawLine(
                                color = Color(0xFFADAEAF),
                                start = Offset(startX, 0f),
                                end = Offset(startX, size.height),
                                strokeWidth = barWidthPX
                            )
                        }

                        // 3条水平线
                        val horizontalLines = 3
                        val horizontalSize = size.height / (horizontalLines + 1)
                        repeat(horizontalLines) { i ->
                            val startY = horizontalSize * (i + 1)
                            drawLine(
                                color = Color(0xFFADAEAF),
                                start = Offset(0f, startY),
                                end = Offset(size.width, startY),
                                strokeWidth = barWidthPX
                            )
                        }
                        clipRect(right = size.width * animationProgress.value) {
                            drawPath(path = path, color = Color.Green, style = Stroke(2.dp.toPx()))
                            drawPath(path = filledPath, brush = brush, style = Fill)
                        }
                    }
                }
        )
    }
}

private fun generateSmoothPath(data: List<Balance>, size: Size): Path {
    val path = Path()
    val numberEntries = data.size - 1
    val weekWidth = size.width / numberEntries
    val max = data.maxBy { it.amount }
    val min = data.minBy { it.amount }
    val range = max.amount - min.amount
    val heightPxPerAmount = size.height / range.toFloat()

    var previousBalanceX = 0f
    var previousBalanceY = size.height

    data.forEachIndexed { i, balance ->
        if (i == 0) {
            path.moveTo(
                x = 0f,
                y = size.height - (balance.amount - min.amount).toFloat() * heightPxPerAmount
            )
        }
        val balanceX = i * weekWidth
        val balanceY = size.height - (balance.amount - min.amount).toFloat() * heightPxPerAmount
        val controlPoint1 = PointF((balanceX + previousBalanceX) / 2f, previousBalanceY)
        val controlPoint2 = PointF((balanceX + previousBalanceX) / 2f, balanceY)
        path.cubicTo(
            controlPoint1.x, controlPoint1.y, controlPoint2.x, controlPoint2.y, balanceX, balanceY
        )
        previousBalanceX = balanceX
        previousBalanceY = balanceY
    }
    return path
}


private fun generatePath(data: List<Balance>, size: Size): Path {
    val path = Path()
    val numberEntries = data.size - 1
    val weekWidth = size.width / numberEntries
    val max = data.maxBy { it.amount }
    val min = data.minBy { it.amount }
    val range = max.amount - min.amount
    val heightPxPerAmount = size.height / range.toFloat()

    data.forEachIndexed { i, balance ->
        if (i == 0) {
            path.moveTo(
                x = 0f,
                y = size.height - (balance.amount - min.amount).toFloat() * heightPxPerAmount
            )
        }
        val balanceX = i * weekWidth
        val balanceY = size.height - (balance.amount - min.amount).toFloat() * heightPxPerAmount
        path.lineTo(x = balanceX, y = balanceY)
    }
    return path
}

@RequiresApi(Build.VERSION_CODES.O)
val graphData = listOf(
    Balance(LocalDate.now(), BigDecimal(65631)),
    Balance(LocalDate.now().plusWeeks(1), BigDecimal(65931)),
    Balance(LocalDate.now().plusWeeks(2), BigDecimal(65851)),
    Balance(LocalDate.now().plusWeeks(3), BigDecimal(65931)),
    Balance(LocalDate.now().plusWeeks(4), BigDecimal(66484)),
    Balance(LocalDate.now().plusWeeks(5), BigDecimal(67684)),
    Balance(LocalDate.now().plusWeeks(6), BigDecimal(66684)),
    Balance(LocalDate.now().plusWeeks(7), BigDecimal(66984)),
    Balance(LocalDate.now().plusWeeks(8), BigDecimal(70600)),
    Balance(LocalDate.now().plusWeeks(9), BigDecimal(71600)),
    Balance(LocalDate.now().plusWeeks(10), BigDecimal(72600)),
    Balance(LocalDate.now().plusWeeks(11), BigDecimal(72526)),
    Balance(LocalDate.now().plusWeeks(12), BigDecimal(72976)),
    Balance(LocalDate.now().plusWeeks(13), BigDecimal(73589)),
)


data class Balance(val date: LocalDate, val amount: BigDecimal)

val PurpleBackgroundColor = Color(color = 0xFF5E35B1)
val BarColor = Color.White.copy(alpha = 0.3f)
val HeightLightColor = Color.White.copy(alpha = 0.7f)